#define FUSE_USE_VERSION 26

#include <errno.h>
#include <fcntl.h>
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>


static const char *hello_str = "Hello World!\n";
static const char *hello_path = "/hello";

static int
_getattr(char const *path, struct stat *stbuf) {
	int res = 0;
	memset(stbuf, 0, sizeof(struct stat));
	if (strcmp(path, "/") == 0) {
		stbuf->st_mode = S_IFDIR | 0755;
		stbuf->st_nlink = 2;
	} else if (strcmp(path, hello_path) == 0) {
		stbuf->st_mode = S_IFREG | 0444;
		stbuf->st_nlink = 1;
		stbuf->st_size = strlen(hello_str);
	} else
		res = - ENOENT;

	return (res);
}

static int
_readdir(char const *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
	if (strcmp(path, "/") != 0)
		return (- ENOENT);
	filler(buf, ".", NULL, 0);
	filler(buf, "..", NULL, 0);
	filler(buf, hello_path + 1, NULL, 0);

	return (0);
}

static int
_open(char const *path, struct fuse_file_info *fi) {
	if (strcmp(path, hello_path) != 0)
		return (- ENOENT);
	if ((fi->flags & 3) != O_RDONLY) {
		return (- EACCES);
	}

	return (0);
}

static int
_read(char const *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
	size_t len;
	
	if (strcmp(path, hello_path) != 0)
		return (- ENOENT);

	len = strlen(hello_str);
	if (offset < len) {
		if (offset + size > len)
         		size = len - offset;
		memcpy(buf, hello_str + offset, size);
	} else
	      size = 0;

	return (size);
}

static struct fuse_operations mediafs_oper = {
	.getattr = _getattr,
	.readdir = _readdir,
	.open = _open,
	.read = _read
};

static const char *output_fname = "/home/bushman/everlasting.wav";
/*
int
main(int argc, char **argv) {
	printf("starting\n");
//	return (fuse_main(argc, argv, &mediafs_oper, NULL));

	av_register_all();

	AVFormatContext *ictx;
	if (av_open_input_file(&ictx, "/home/bushman/torrents/Tom Petty - She is The One/CDImage.ape", NULL, 0, NULL) != 0)
		return (-1);
	printf("header read\n");
	if (av_find_stream_info(ictx) < 0)
		return (-1);
	printf("input stream found\n");
	dump_format(ictx, 0, "", 0);

	int i;
	AVFormatContext *octx;
	AVOutputFormat *ofmt;
	AVStream *audio_ostr;
	AVCodecContext *ocodec_ctx;

	int audio_stream_index = -1;
	for (i = 0; i < ictx->nb_streams; ++i) {
		printf("checking stream: %d of %d\n", i, ictx->nb_streams);
		if (ictx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) {
			audio_stream_index = i;
			break;
		}
	}
	if (audio_stream_index == -1) {
		printf("no audio input stream\n");
		exit(1);
	}
	AVCodecContext *icodec_ctx;
	AVCodec *icodec;
	icodec_ctx = ictx->streams[audio_stream_index]->codec;
	icodec = avcodec_find_decoder(icodec_ctx->codec_id);
	avcodec_open(icodec_ctx, icodec);
//	av_seek_frame(ictx, audio_stream_index, AV_NOPTS_VALUE, AVSEEK_FLAG_BACKWARD);
//
//	AVPacket last_frame;
//	av_read_frame(ictx, &last_frame);
//	printf("audio input duration: %d\n", last_frame.dts);
//	av_seek_frame(ictx, audio_stream_index, 0, 0);

	octx = av_alloc_format_context();
	ofmt = guess_format(NULL, output_fname, NULL);
	octx->oformat = ofmt;
	audio_ostr = av_new_stream(octx, 1);
	ocodec_ctx = audio_ostr->codec;
	ocodec_ctx->codec_id = CODEC_ID_PCM_S16LE;
	ocodec_ctx->codec_type = CODEC_TYPE_AUDIO;
	ocodec_ctx->bit_rate = icodec_ctx->bit_rate;
	ocodec_ctx->sample_rate = icodec_ctx->sample_rate;
	ocodec_ctx->channels = icodec_ctx->channels;
	av_set_parameters(octx, NULL);
	url_fopen(&octx->pb, output_fname, URL_WRONLY);
	av_write_header(octx);




	uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
	uint8_t audio_outbuf[(FF_MIN_BUFFER_SIZE * 3) / 2];

	AVPacket packet;
	while (av_read_frame(ictx, &packet) >= 0) {
		if (packet.stream_index == audio_stream_index) {
			int audio_packet_size = packet.size;
			uint8_t *audio_packet_data = packet.data;

			while (audio_packet_size > 0) {
//				printf("audio packet\n");
				int data_size = sizeof(audio_buf);
				int len1 = avcodec_decode_audio2(icodec_ctx, (int16_t *)audio_buf, &data_size, audio_packet_data, audio_packet_size);
				if (len1 < 0) {
					printf("broken packet\n");
					break;
				}

				audio_packet_data += len1;
				audio_packet_size -= len1;
				if (data_size <= 0) {
//					printf("no data yet\n");
					continue;
				}
//				printf("got audio: %d\n", data_size);

				AVPacket out_packet;
				av_init_packet(&out_packet);
//				out_packet.size = avcodec_encode_audio(octx_codec, audio_outbuf, sizeof(audio_outbuf), audio_buf);

				out_packet.flags |= PKT_FLAG_KEY;
				out_packet.stream_index = audio_ostr->index;
				out_packet.data = audio_buf;
				out_packet.size = data_size;
				if (av_write_frame(octx, &out_packet) != 0) {
					printf("shit!\n");
					exit(1);
				}
			}
				
		}
		av_free_packet(&packet);
	}
//	avcodec_close(icodec);
 

//	avcodec_close(audio_ostr->codec);
	av_write_trailer(octx);
	for (i = 0; i < octx->nb_streams; ++i) {
		av_freep(&octx->streams[i]);
	}
	url_fclose(octx->pb);
	av_free(octx);

	return 0;
}
*/

#include <assert.h>
#include "converters.h"
#include "pcm.h"
#include "file_mapper.h"

static struct file_mapper *
create_test_file_mapper(char *buffer, size_t bufsize)
{
	struct mediafs_decoder_stat decoder_stat;
	struct pcm_encoder_params encoder_params;

	struct mediafs_decoder *decoder;
	struct mediafs_encoder *encoder;
	struct mediafs_decoder_ctx *decoder_ctx;
	struct mediafs_encoder_ctx *encoder_ctx;
	struct file_mapper *result;
	int res;

	printf("buffer: %p, size: %lld\n", buffer, bufsize);
	decoder = get_mediafs_flac_decoder();
	decoder_ctx = mediafs_decoder_open(decoder, buffer, bufsize);
	printf("decoder opened\n");

	memset(&decoder_stat, 0, sizeof(decoder_stat));
	res = mediafs_decoder_get_stat(decoder_ctx, &decoder_stat);
	assert(res == 0);

	encoder = get_mediafs_pcm_encoder();
	fill_mediafs_pcm_encoder_params(&encoder_params, &decoder_stat);
	encoder_ctx = mediafs_encoder_open(encoder, (struct mediafs_encoder_params *)&encoder_params);

	result = file_mapper_open(decoder_ctx, encoder_ctx);
	assert(result != NULL);
	return result;
}

static void
destroy_test_file_mapper(struct file_mapper *fmapper)
{
	mediafs_decoder_close(fmapper->decoder_ctx);
	mediafs_encoder_close(fmapper->encoder_ctx);
	file_mapper_close(fmapper);
}

#include <sys/mman.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>

#define INPUT_FILE_PATH "/home/bushman/torrents/1998 This Is My Truth Tell Me Yours (flac)/Manic Street Preachers This Is My Truth Tell Me Yours 01 The Everlasting.flac"
#define OUTPUT_FILE_PATH "/home/bushman/everlasting.wav"
#define CHUNK_SIZE 8192

#include "cue_lexer_parser.h"

int
main(int argc, char **argv)
{
/*	char *buffer;
	struct file_mapper *fmapper;
	struct stat st;
	int ifd, ofd;

	ifd = open(INPUT_FILE_PATH, O_RDONLY);
	fstat(ifd, &st);
	printf("input file size: %lld\n", st.st_size);

	buffer = mmap(NULL, st.st_size, PROT_READ, 0, ifd, 0);
	assert(buffer != MAP_FAILED);
	printf("buffer mapped\n");
	fmapper = create_test_file_mapper(buffer, st.st_size);
	printf("projected file size: %d\n", file_mapper_get_mapped_size(fmapper));
	
	size_t projected_size = file_mapper_get_mapped_size(fmapper);
	char mapped_buffer[CHUNK_SIZE];
	size_t offset, min_size;
	ofd = open(OUTPUT_FILE_PATH, O_WRONLY | O_CREAT);
	offset = 0;
	while (offset < projected_size) {
		min_size = MIN(CHUNK_SIZE, projected_size - offset);
		file_mapper_get_mapped_bytes(fmapper, offset, min_size, mapped_buffer);	
		printf("writing chunk %lld\n", min_size);
		write(ofd, mapped_buffer, min_size);
		offset += min_size;
	}
	close(ofd);

	close(ifd);*/

/*	void *scanner;
	yylex_init(&scanner);
	yyset_debug(1, scanner);
	yyparse(scanner);
	yylex_destroy(scanner);*/
	
	return 0;
}

